#include <iostream>
#include <sys/types.h>
#include <netinet/in.h> // 主要定义IPv4地址结构(sockaddr_in)和IP协议相关常量
#include <sys/socket.h> // 定义通用套接字结构(sockaddr)和socket相关函数
#include <strings.h>
#include <string.h>
#include <string>
#include <functional>
#include "Log.hpp"

/*---------------- 服务端代码实现 --------------*/

using namespace LogModule;
//这里参数需要const修饰 涉及到权限放大问题  不加const修饰会报错
using func_t = std::function<std::string(const std::string&)>;

class UdpServer
{
public:
    UdpServer(uint16_t port,func_t func) : _port(port),
                               _sockfd(-1),
                               _isrunning(false),
                               _func(func)
    {
    }

    void Init()
    {
        // 1、创建套接字

        /*
        @参数1：地址族 AF_INET(IPV4)
        @参数2：套接字类型 SOCK_DGRAM(无连接 -> UDP)
        @参数3：通常设为0 因为前两个参数组合唯一协议时，系统会自动选择
        */
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error!";
            exit(1);
        }
        LOG(LogLevel::INFO) << "socket success";

        // 2、绑定端口号和ip
        struct sockaddr_in local; // IPV4地址的结构体
        bzero(&local, sizeof(local));
        // 2.1 ipv4初始化
        local.sin_family = AF_INET;         // 地址族：表示使用IPV4协议
        local.sin_port = _port;      //(我这里的机器是大端，所以不用转) 端口号: 这里要看机器是大端还是小端 如果机器是小段那么调用htons将小端转成大端 (网络传输规定必须使用打端)
        local.sin_addr.s_addr = INADDR_ANY; // 设置IP地址：这里可以填写具体的IP地址，但是不建议这么做，这样操作会只跟该IP通讯，其他IP无法通讯。
        // 2.2 bind 绑定 (IP + 端口)
        int n = bind(_sockfd, (const sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error!";
            exit(2);
        }
        LOG(LogLevel::INFO) << "bind success";
    }

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            char buff[1024];
            memset(buff, 0, sizeof(buff));
            // 1、接收数据
            struct sockaddr_in peer; // 存储发送方的地址信息
            socklen_t len = sizeof(peer);

            int n = recvfrom(_sockfd, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&peer, &len);
            //大于0表示接收成功 接收到的数据个数为n
            if (n > 0)
            {
                
                //  一会在查看时那个ip + 端口号 发的
                LOG(LogLevel::DEBUG) << "内容：" << buff;

                // 2、测试 -> 将接收到的数据发送到客户端 (数据处理)
                buff[n] = 0;
                std::string ret = _func(buff);
                sendto(_sockfd,ret.c_str(),ret.size(),0,(struct sockaddr*)&peer,len);
            }

        }
    }

private:
    uint16_t _port;  // 端口号
    int _sockfd;     // 创建套接字返回值 -> 套接字描述符
    bool _isrunning; // UDP开启标志位
    func_t _func;
    
};